/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.icanci.loopstack.damc.core.dynamic.visitor;

import static damc.objectweb.asm.Opcodes.ASM9;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import cn.icanci.loopstack.damc.spi.model.refresh.DynamicCondition;
import damc.objectweb.asm.Label;
import damc.objectweb.asm.MethodVisitor;
import damc.objectweb.asm.Opcodes;
import damc.objectweb.asm.Type;

/**
 * http://localhost:9090/refresh/refresh
 * http://localhost:9090/order/create
 * 
 * @author icanci
 * @since 1.0 Created in 2025/04/21 16:00
 */
public class VisitorModifier extends MethodVisitor {

    private int                         currentLine = -1;
    /** 方法名*/
    private final String                methodName;
    /** 签名(无泛型): (Ljava/lang/String;Ljava/lang/Integer;Lcn/loopstack/damc/springweb/controller/OrderController;Ljava/util/Set;)Ljava/lang/String; */
    private final String                descriptor;
    /** mock 的 动态条件 */
    private final Set<DynamicCondition> conditions;
    /** 被 mock 的class */
    private final Class<?>              mockedClazz;
    private final boolean               isStaticMethod;

    public VisitorModifier(MethodVisitor mv, int access, String name, String descriptor, List<DynamicCondition> conditions, Class<?> clazz) {
        super(ASM9, mv);
        this.conditions = new HashSet<>(conditions);
        this.methodName = name;
        this.descriptor = descriptor;
        this.mockedClazz = clazz;
        this.isStaticMethod = (access & Opcodes.ACC_STATIC) != 0;
    }

    @Override
    public void visitLineNumber(int line, Label start) {
        this.currentLine = line;
        super.visitLineNumber(line, start);
    }

    @Override
    public void visitCode() {
        super.visitCode();
        // 对函数进行匹配
        DynamicCondition targetCondition = new DynamicCondition(methodName, descriptor);
        if (!conditions.contains(targetCondition)) {
            return;
        }
        Type returnType = Type.getReturnType(descriptor);
        // TODO 基本数据类型不支持
        if (isBasicDataType(returnType)) {
            return;
        }
        // insert method
        insertInnerClassInvoke(mv, descriptor);
    }

    private void insertInnerClassInvoke(MethodVisitor mv, String methodDescriptor) {
        Type[] argumentTypes = Type.getArgumentTypes(methodDescriptor);

        // 处理实例参数 (非静态方法传递 `this`，静态方法传递 `null`)
        if (isStaticMethod) {
            mv.visitInsn(Opcodes.ACONST_NULL); // 静态方法传递 null
        } else {
            mv.visitVarInsn(Opcodes.ALOAD, 0); // 非静态方法传递 `this`
        }

        // 处理方法参数
        loadMethodArguments(mv, argumentTypes);
        // 加载全限定类名
        mv.visitLdcInsn(mockedClazz.getName());

        // 加载方法名
        mv.visitLdcInsn(methodName);

        // signature
        mv.visitLdcInsn(descriptor);

        // 调用 InnerClass.invoke(Object _this, Object[] args)
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, //
            "cn/icanci/loopstack/damc/core/service/MockRunnerService", // 类名
            "invoke", // 方法名
            "(Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;", // 方法描述符
            false);

        // 如果方法返回类型不是 void，处理返回值
        Type returnType = Type.getReturnType(methodDescriptor);
        if (returnType.getSort() == Type.VOID) {
            mv.visitInsn(Opcodes.RETURN); // void 方法直接返回
        } else {
            // returnType.getClassName()
            mv.visitTypeInsn(Opcodes.CHECKCAST, returnType.getInternalName()); // 类型转换
            mv.visitInsn(returnType.getOpcode(returnType.getSort())); // 返回值指令
        }
    }

    /**
     * loadMethodArguments 装载函数
     * 
     * @param mv mv
     * @param argumentTypes argumentTypes
     */
    private void loadMethodArguments(MethodVisitor mv, Type[] argumentTypes) {
        // 创建一个数组来存储参数
        mv.visitIntInsn(Opcodes.BIPUSH, argumentTypes.length); // 数组大小
        mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); // 创建 Object[] 数组

        int localIndex = isStaticMethod ? 0 : 1; // 局部变量索引 (跳过 `this`)

        for (int i = 0; i < argumentTypes.length; i++) {
            mv.visitInsn(Opcodes.DUP); // 复制数组引用
            mv.visitIntInsn(Opcodes.BIPUSH, i); // 数组索引

            // 加载参数值
            Type argumentType = argumentTypes[i];
            mv.visitVarInsn(argumentType.getOpcode(Opcodes.ILOAD), localIndex);
            localIndex += argumentType.getSize(); // 更新局部变量索引

            // 基本类型装箱
            boxPrimitiveType(mv, argumentType);

            // 存储到数组中
            mv.visitInsn(Opcodes.AASTORE);
        }
    }

    /**
     * box it
     * 
     * @param mv mv
     * @param type type
     */
    private void boxPrimitiveType(MethodVisitor mv, Type type) {
        switch (type.getSort()) {
            case Type.INT:
                mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
                break;
            case Type.LONG:
                mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
                break;
            case Type.FLOAT:
                mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
                break;
            case Type.DOUBLE:
                mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
                break;
            case Type.BOOLEAN:
                mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
                break;
            case Type.BYTE:
                mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false);
                break;
            case Type.CHAR:
                mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false);
                break;
            case Type.SHORT:
                mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false);
                break;
            default:
                // 非基本类型无需装箱
        }
    }

    /**
     * box it
     *
     * @param type type
     */
    private boolean isBasicDataType(Type type) {
        switch (type.getSort()) {
            case Type.INT:
            case Type.LONG:
            case Type.FLOAT:
            case Type.DOUBLE:
            case Type.BOOLEAN:
            case Type.BYTE:
            case Type.CHAR:
            case Type.SHORT:
                return true;
        }
        return false;
    }

}
